local util = require "util"
local error_code = require "error_code"
local constant = require "constant"
local math_abs = math.abs
local table_insert = table.insert
local table_sort = table.sort
local table_remove = table.remove
local math_floor = math.floor
local table_unpack = table.unpack
local math_random = math.random
--math.randomseed(os.time())

--输赢类型
local SIDE_LOONG    = 1    --龙
local SIDE_PHOENIX  = 2    --凤
local SIDE_EQUAL    = 3    --平局
local SIDE_RANDOM   = 4    --随机

--游戏状态定义
local GAME_STATUS_INIT = 0          --初始化状态
local GAME_STATUS_STARTED = 1       --游戏开始
local GAME_STATUS_BETTING = 2       --押注
local GAME_STATUS_OVER = 3          --结束状态

local AVAILABLE_SIDES_MAP = {
    [SIDE_EQUAL] = true,
    [SIDE_LOONG] = true,
    [SIDE_PHOENIX] = true
}

local WIN_MULTIPLE = {
    [SIDE_EQUAL] = 8,
    [SIDE_LOONG] = 1,
    [SIDE_PHOENIX] = 1
}

---打印--
local not_robot_uid = 0

--输赢类型
local card_type = {}

local LF_INDEX = {}
local M = {}

local function make_card_id(sign,number)
    return sign * 100 + number
end

local function get_card_number(card_id)
    return card_id % 100
end

local function get_card_color(card_id)
    return math_floor(card_id / 100)
end

--[[
    获取数字一样，花色不一样的牌
]]
local function get_same_card_number(card_id)
    local color = get_card_color(card_id)
    local num = get_card_number(card_id)
    color = (color + 1) % 4
    if color == 0 then
        color = 1
    end

    return make_card_id(color,num)
end

function LF_INDEX:init()
    --self.room_instance = room_instance
    local cards = {}
    for i = 1,13 do
        for j = 1,4 do
            table_insert(cards,make_card_id(j,i))
        end
    end  

    self.game_status = GAME_STATUS_INIT
    self.cards = cards
    self.betting_list = {
        [SIDE_EQUAL] = 0,
        [SIDE_LOONG] = 0,
        [SIDE_PHOENIX] = 0
    }
    self.betting_list_no_robot = {
        [SIDE_EQUAL] = 0,
        [SIDE_LOONG] = 0,
        [SIDE_PHOENIX] = 0
    }

    self.player_betting_map = {}            --当前游戏中的玩家列表
    self.side_gold_list     = {}
    self.game_result        = {}
    self.robot_betting_list = {
        [SIDE_EQUAL] = {},
        [SIDE_LOONG] = {},
        [SIDE_PHOENIX] = {},
    }
end

function LF_INDEX:check_bet_num(player_list,player_not_bet_num)
    local ret5 = {}
    local ret4 = {}
    for uid,info in pairs(player_list) do
        if not self.player_betting_map[uid] then
            player_not_bet_num[uid] = player_not_bet_num[uid] or 0
            player_not_bet_num[uid] = player_not_bet_num[uid] + 1
            if player_not_bet_num[uid] >= 5 then
                table_insert(ret5,uid)
            end
            if player_not_bet_num[uid] == 4 then
                table_insert(ret4,uid)
            end
        else
            player_not_bet_num[uid] = 0
        end
    end
    for uid,num in pairs(player_not_bet_num) do
        if not player_list[uid] then
            player_not_bet_num[uid] = nil
        end
    end    
    return ret5,ret4
end
function LF_INDEX:start_game()
    assert(self.game_status == GAME_STATUS_INIT)
    self.game_status = GAME_STATUS_STARTED

    return true
end

function LF_INDEX:deal()
    assert(self.game_status == GAME_STATUS_STARTED)
end

function LF_INDEX:shuffle(swap_times)
    assert(self.game_status == GAME_STATUS_STARTED)
    --[[
    local cards = self.cards
    swap_times = swap_times or 3
    local card_count = #cards
    for j=1,swap_times do
        for i = 1,card_count do
            local n = math_random(1,card_count)
            local m = math_random(1,card_count)
            if n ~= m then
                cards[n],cards[m] = cards[m],cards[n]
            end
        end
    end

    for i = 1,51 do
        local long = get_card_number(cards[i])
        local feng = get_card_number(cards[i+1])
        local card = {long,feng}
        local type
        if long == feng then
            type = card_equal
        elseif long > feng then
            type = card_Loong
        else
            type = card_phoenix
        end

        if not card_type[type] then
            card_type[type] = {}
            table_insert(card_type[type], card)
        else
            table_insert(card_type[type], card)
        end
    end
    ]]

end

function LF_INDEX:start_betting()
    assert(self.game_status == GAME_STATUS_STARTED)
    self.game_status = GAME_STATUS_BETTING
    return true
end

function LF_INDEX:get_multiple(side) --获取赔率
    if WIN_MULTIPLE[side] then
        return WIN_MULTIPLE[side]
    end

    return 1
end

function LF_INDEX:is_betting()
    return self.game_status == GAME_STATUS_BETTING
end

function LF_INDEX:init_side_result(side_result)
    for side = 1, 3 do
        side_result[side] = { side = side, result_coins = 0}
    end
end

function LF_INDEX:init_player_result(uid, bet_result_list,pay_fee_map)

    local uid_bet_result = {}
    --local side_result = {}

    bet_result_list[uid] = uid_bet_result

    --self:init_side_result(side_result)
    uid_bet_result.side_result = {} --玩家收益详情
    uid_bet_result.total_coins = 0 --玩家总收益
    uid_bet_result.uid = uid    --玩家uid
    pay_fee_map[uid] = 0

end

function LF_INDEX:judge_result(win_side,fee_percent)
    assert(self.game_status == GAME_STATUS_BETTING)
   
    local loong_card
    local feng_card
    local win
    local system_win_coins = 0

    win,loong_card,feng_card = self:get_side_card(win_side)
    dbglog("lfdj_judge_result===",win_side,win,loong_card,feng_card)

    local bet_result_list = {}
    local pay_fee_map = {}
    local sys_win_lose = {} --目的是为了机器人的输赢不加减系统庄家的钱
    local winner_coin_map = {} --赢

    for uid,records in pairs(self.player_betting_map) do
        self:init_player_result(uid, bet_result_list,pay_fee_map)
        local uid_bet_result = bet_result_list[uid]
        
        sys_win_lose[uid] = 0
        winner_coin_map[uid] = 0

        for side, r in pairs(records) do
            local uid_side_result = uid_bet_result.side_result
            if win == side then --赢钱方
                local biesu = self:get_multiple(win)
                local win_coins = r.coins * biesu
                local fee = math_floor(win_coins * fee_percent)    --玩家赢钱数(扣除台费抽水)
                uid_bet_result.total_coins = uid_bet_result.total_coins + win_coins - fee --记录下 玩家总的输赢
                table_insert(uid_side_result,{side = side,result_coins = win_coins})
                pay_fee_map[uid] = fee --记录下 玩家台费抽水
                sys_win_lose[uid] = sys_win_lose[uid] -  win_coins          --玩家赢钱，系统就输钱
                winner_coin_map[uid] = winner_coin_map[uid] + win_coins + r.coins - fee
            else
                if win == SIDE_EQUAL then --如果是开的平局,退还玩家押注的钱
                    uid_bet_result.total_coins = uid_bet_result.total_coins
                    table_insert(uid_side_result,{side = side,result_coins = r.coins})
                    winner_coin_map[uid] = winner_coin_map[uid] + r.coins
                    --这里 机器人如果压的平 处理
                else
                    uid_bet_result.total_coins = uid_bet_result.total_coins - r.coins
                    table_insert(uid_side_result,{side = side,result_coins = -r.coins})
                    sys_win_lose[uid] = sys_win_lose[uid] +  r.coins --玩家赢钱，系统就输钱
                end                
            end
        end
    end
    
    -- for uid,uid_bet_result in pairs(bet_result_list) do
    --    if uid_bet_result.total_coins > 0 then
    --         fee = math_floor(uid_bet_result.total_coins * fee_percent)
    --         uid_bet_result.total_coins = uid_bet_result.total_coins - fee
    --         winner_coin_map[uid] = winner_coin_map[uid] - fee
    --         pay_fee_map[uid] = fee 
    --    end
    -- end
        --[[测试使用
        if not_robot_uid == uid then
            print('------records---------')
            print_r(records)
            print('------uid_bet_result---------')
            print_r(uid_bet_result)
        end        
        ]]

    self.game_status = GAME_STATUS_OVER
    self.game_result = {
        winner_side       = win,
        loong_card        = loong_card,
        phoenix_card      = feng_card,
        bet_result_list   = bet_result_list,
        pay_fee_map       = pay_fee_map,
        sys_win_lose      = sys_win_lose,
        winner_coin_map   = winner_coin_map,
    }
    dbglog("lfdj_game_result_record",win,loong_card,feng_card)

    self.basechip = nil
    self.player_bet_flottant = nil
    
    return true
end

function LF_INDEX:load_other_cnf(basechip,flottant)
	self.basechip 				= basechip
	self.player_bet_flottant 	= math_random(-flottant,flottant)
end

local function extract_card_number(card_id)
    return card_id % 100
end

function LF_INDEX:compare_card(loong_card,feng_card)
    if extract_card_number(feng_card) == extract_card_number(loong_card) then
        return 3
    end

    if extract_card_number(feng_card) > extract_card_number(loong_card) then
        return 2
    end

    return 1
end

--[[
    获取牌
    card_type,控制输赢。3-平局,1-龙赢,2-凤赢，4-随机
]]
function LF_INDEX:get_side_card(card_type)
    local win_side
    if card_type and card_type <=3  and card_type >= 1 then
        win_side = card_type
    else
        win_side = SIDE_RANDOM
    end
    
    local cards = self.cards
    local long_index = math_random(1,#cards)
    local loong_card = cards[long_index]
    local feng_card

    local loong_num =  get_card_number(loong_card) --龙牌 点数
    local feng_num =  0 --凤牌 点数
    if win_side == SIDE_EQUAL then --如果是平局，同一张牌 不同花色
        feng_card = get_same_card_number(loong_card)
    elseif win_side == SIDE_RANDOM then
        local find = false
        repeat --找凤牌
            feng_card = cards[math_random(1,#cards)]    --随机找一张跟龙牌不一样的凤牌
            if feng_card ~= loong_card then
                find = true
            end
        until(find)
        win_side = self:compare_card(loong_card,feng_card)
        print("get_side_card_random====aaaaa",loong_card,feng_card,win_side)
    else
        local find = false
        repeat --找凤牌
            feng_card = cards[math_random(1,#cards)]
            feng_num = get_card_number(feng_card)
            if feng_num ~= loong_num then
                find = true
            end
        until(find)
       
        if win_side == SIDE_LOONG then --如果龙赢
            if feng_num > loong_num then 
                feng_card,loong_card = loong_card,feng_card
            end
        else    --凤赢
            if loong_num > feng_num then 
                feng_card,loong_card = loong_card,feng_card
            end
        end
    end

    dbglog("get_side_card---->win_side--loong_card--feng_card--loong_num--feng_num", win_side,loong_card,feng_card,loong_num,feng_num)
    return win_side,loong_card,feng_card
end

function LF_INDEX:get_all_betting_list()
    local ret = {}
    for side,coins in pairs(self.betting_list) do
        ret[side] = coins
    end
    
    return ret
end 

function LF_INDEX:get_side_betting_total_coins(side)
    return self.betting_list[side] or 0
end

function LF_INDEX:get_side_player_bett_coins(side)
    return self.betting_list_no_robot[side] or 0
end

function LF_INDEX:get_side_player_bet_list(side)
    local side_bet_list = {}
    for uid,records in pairs(self.player_betting_map) do
        if records[side] then
            table_insert(side_bet_list,{uid = uid,bet_coins = records[side].coins or 0})
        end
    end

    return side_bet_list
end

function LF_INDEX:get_my_betting_list(uid)
    local player_betting_map = self.player_betting_map
    local records = player_betting_map[uid]
    if not records then
        return
    end
    
    local ret = {}
    for side,info in pairs(records) do
        ret[side] = info.coins
    end
    
    return ret
end

function LF_INDEX:get_player_bet_coins()
    local player_coins_map = {}
    for uid,records in pairs(self.player_betting_map) do
        local player_coins = 0
        for _,t in pairs(records) do
            player_coins = player_coins + t.coins
        end
        player_coins_map[uid] = player_coins
    end

    return player_coins_map
end

function LF_INDEX:get_my_betting_side(uid,side)

    local player_betting_map = self.player_betting_map
    local records = player_betting_map[uid]
    if not records then
        return 0
    end

    local r = records[side] or {coins = 0}
    return r.coins
end

function LF_INDEX:get_gold_list()
    local ret = {}
    for side,gold_num in pairs(self.side_gold_list) do
        ret[side] = gold_num
    end
    return ret
end

function LF_INDEX:get_betting_uid_list()
    local betting_uid_list = {}
    for uid,_ in pairs(self.player_betting_map) do
        table_insert(betting_uid_list,uid)
    end
    return betting_uid_list
end

function LF_INDEX:get_game_result()
    assert(self.game_status == GAME_STATUS_OVER)
    return self.game_result
end

function LF_INDEX:get_betting_list_no_robot()
    return self.betting_list_no_robot
end

function LF_INDEX:get_side_betting_total_coins(side) 
    return self.betting_list[side]
end

function LF_INDEX:get_side_robot_type_betting_coins(side,coins_type)
    return self.robot_betting_list[side][coins_type] or 0
end

function LF_INDEX:get_total_betting_coins()
    local total_coins = 0
    for side,coins in pairs(self.betting_list) do
        total_coins = total_coins + coins
    end
    return total_coins
end

function LF_INDEX:is_available_betting(coins,available_betting_list) --押注金额是否有效
    --local betting_list = constant.LFDJ_AVAILABLE_BET_COINS_LIST
    for i = 1,#available_betting_list do
        if available_betting_list[i] ==  coins then
            return true
        end
    end

    return false
end

function LF_INDEX:is_personal_limit(uid,side,coins,self_table_type)
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata or not roomdata.person_limit then return false end

    if roomdata.person_limit[1] and roomdata.person_limit[1] ~= 0 and coins < roomdata.person_limit[1] then 
        return true
    end
    local all_coins = coins
    local records = self.player_betting_map[uid] or {}
    for side,info in pairs(records) do
        all_coins = all_coins + info.coins
    end
    if roomdata.person_limit[2] and roomdata.person_limit[2] ~= 0 and all_coins > roomdata.person_limit[2] then
        return true
    end

    return false
end

function LF_INDEX:is_personal_side_limit(uid,side,coins,self_table_type)
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata then return false end

    local records = self.player_betting_map[uid] or {}

    if roomdata.loong_limit and roomdata.loong_limit ~= 0 and side == SIDE_LOONG and records[SIDE_LOONG] and records[SIDE_LOONG].coins + coins > roomdata.loong_limit then
        return true
    end
    if roomdata.phoenix_limit and roomdata.phoenix_limit ~= 0 and side == SIDE_PHOENIX and records[SIDE_PHOENIX] and records[SIDE_PHOENIX].coins + coins > roomdata.phoenix_limit then
        return true
    end
    if roomdata.equal_limit and roomdata.equal_limit ~= 0 and side == SIDE_EQUAL and records[SIDE_EQUAL] and records[SIDE_EQUAL].coins + coins > roomdata.equal_limit then
        return true
    end
    return false
end

function LF_INDEX:is_side_limit(side,coins,self_table_type)
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata then return false end

    if roomdata.difference_limit and roomdata.difference_limit ~= 0 then
        if side == SIDE_LOONG and math_abs(self.betting_list[side] + coins - self.betting_list[SIDE_PHOENIX]) > roomdata.difference_limit then
            return true
        elseif side == SIDE_PHOENIX and math_abs(self.betting_list[side] + coins - self.betting_list[SIDE_LOONG]) > roomdata.difference_limit then
            return true
        end
    end

    if roomdata.all_equal_limit and side == SIDE_EQUAL and roomdata.all_equal_limit ~= 0 and self.betting_list[side] + coins > roomdata.all_equal_limit then
        return true
    end
    return false
end

function LF_INDEX:get_uid_bet_total(uid)
    local total = 0
    local my_betting_list = self:get_my_betting_list(uid)
    if my_betting_list then
        for _,coins in pairs(my_betting_list) do
            total = total + coins
        end
    end

    return total
end

function LF_INDEX:can_bet(uid,side,coins,available_betting_list,bet_type,self_table_type)
    if not AVAILABLE_SIDES_MAP[side] then
        return false,-301
    end

    if bet_type ~= constant.BET_TYPE_CONTINUE and not self:is_available_betting(coins,available_betting_list) then --押注金额是否有效
        return false,-303
    end

    -- local player_betting_map = self.player_betting_map
    -- local records = player_betting_map[uid]
    -- if not records then
    --     return true
    -- end

    if self:is_side_limit(side,coins,self_table_type) then --某一边是否到最大了
        return false, error_code.SIDE_BET_LIMIT
    end

    if self:is_personal_side_limit(uid,side,coins,self_table_type) then --个人某一边是否最大了
        return false, error_code.PERSONAL_SIDE_BET_LIMIT
    end

    if self:is_personal_limit(uid,side,coins,self_table_type) then --个人某一边是否最大了
        return false, error_code.PERSONAL_BET_LIMIT
    end

    -- local both_side_flags = (1 << SIDE_LOONG) | (1 << SIDE_PHOENIX)
    -- local flags = 0
    -- for side in pairs(records) do
    --     flags = (1 << side) | flags
    -- end  

    -- flags = (1 << side) | flags
    -- if flags & both_side_flags == both_side_flags then
    --     --不可以同时投两方
    --     return false,error_code.LFDJ_CANT_BET_DIFFERENT_SIDE
    -- end
    return true
end

function LF_INDEX:bet(uid,side,coins,is_robot,player_info,bet_type,self_table_type)
    local ok,ret = self:can_bet(uid,side,coins,player_info.available_betting_list,bet_type,self_table_type)
    if not ok then
        return false,ret
    end

    local player_betting_map = self.player_betting_map
    local records = player_betting_map[uid]
    if not records then
        records = {}
        player_betting_map[uid] = records
    end

    local r = records[side] or {coins = 0}
    records[side] = r
    local curr_coins = r.coins 

    r.coins = curr_coins + coins

    local side_coins = self.betting_list[side] + coins
    self.betting_list[side] = side_coins

    --非机器人进行累计
    if not is_robot then
        self.betting_list_no_robot[side] = self.betting_list_no_robot[side] + coins
        ------测试使用---
        --not_robot_uid = uid
    else
        local coins_type = player_info.coins_type
        self.robot_betting_list[side][coins_type] = (self.robot_betting_list[side][coins_type] or 0) + coins
    end

    if coins >= 10000 then
        --增加金砖的记录
        local curr_gold_num = self.side_gold_list[side] or 0
        self.side_gold_list[side] = curr_gold_num + 1
    end

    return true,{
        side_coins = side_coins,
        my_side_coins = r.coins,
    }

end

function M.new()
    local o = {}
    return setmetatable(o,{__index = LF_INDEX})
end

function M.get_random_history(num)
    local ret = {}
    for i = 1,num do
        local rank_index = math_random(1,100)
        if rank_index < 7 then
            table_insert(ret,{side = 3, })
        elseif rank_index < 54 then
            table_insert(ret,{side = 2, })
        else    
            table_insert(ret,{side = 1, })
        end
    end
    -- --测试牌路
    -- local ret1 = {3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,1,2,1,2,1,2,1,2,1,0,0,0,0,0,0,0,0,0,0,0,1,2}
    -- for i,v in ipairs(ret1) do
    --     table_insert(ret,1,{side = v })
    -- end
    return ret
end

M.SIDE_LOONG = SIDE_LOONG
M.SIDE_PHOENIX = SIDE_PHOENIX
M.SIDE_EQUAL = SIDE_EQUAL
M.WIN_MULTIPLE = WIN_MULTIPLE
M.RICH_SEAT_SIDE = 1
M.LUCK_SEAT_SIDE = 4

return M